#ifndef _COMPILATION_TYPE_H_
#define _COMPILATION_TYPE_H_

#include <compilation/Forward.h>

#include <cassert>
#include <iostream>
#include <memory>


namespace compilation
{

class VirtualType
{
public:
  VirtualType() {}
  VirtualType(const VirtualType& source) {}

  virtual ~VirtualType() {}

  virtual int getSize() const = 0;
  virtual VirtualType* clone() const = 0;
  virtual void print(std::ostream& out) const = 0;
};

class BaseType
    : public VirtualType
{
public:

  enum Value
  {
    VUndefined,
    VInt,
    VChar
  };

private:

  Value m_value;

public:

  BaseType(void) : m_value(VUndefined) {}

  BaseType(Value valueSource)
    : m_value(valueSource) {}

  BaseType(const BaseType& source)
    : VirtualType(source), m_value(source.m_value) {}

  virtual int getSize(void) const
  {
    assert(m_value != VUndefined);
    return (m_value == VInt) ? 4 : 1;
  }

  virtual VirtualType* clone() const
  { return new BaseType(*this); }

  virtual void print(std::ostream& out) const
  {
    out << ((m_value == VInt)
            ? "int"
            : ((m_value == VChar)
               ? "char"
               : "no-type"));
  }
};

class PointerType
    : public VirtualType
{
private:

  std::auto_ptr<VirtualType> m_subType;

public:

  PointerType(VirtualType* subType) : m_subType(subType) {}

  PointerType(const PointerType& source)
    : VirtualType(source)
    , m_subType(source.m_subType->clone()) {}

  virtual int getSize() const
  { return 4; }

  virtual VirtualType* clone() const
  { return new PointerType(*this); }

  VirtualType* getSubType()
  { return m_subType.release(); }

  virtual void print(std::ostream& out) const
  {
    m_subType->print(out);
    out << '*';
  }
};

} // compilation

#endif // _COMPILATION_TYPE_H_
